#light

module CGraph.DataGraph
open CGraph.DataSchema
open CGraph.DataModel
open CGraph.DG2
open CGraph.Generic
open CGraph.Generic.Algorithms
open CGraph.IO

(*Binding between the datamodel and the data schema*)
(*The question is how to model reference and reference list nodes
as they aren't explicitely modeled in any other form and they
make their own data graph.  I guess just model them as vertexes
with their own edges and such.
You can view a schema as a graph generator.
This is the implementation of the generated graph.
As I am getting tired of writing infrastructure, I am going to mix
in all the necessary responsibilities at this level then
refactor them later.  This includes attributes (additional data
to further specialize a given property; includes things like
default and such.  Some values have a fixed range and can be clamped;
around, some values have a fixed range and are expected to wrap around.
Some discreet values when animated shouldn't take the shortest paths
between two items numerically but instead should either be one or 
the other.  

Take enumerations.  Lets say have have colorspace inversion system
and it can operate on RGB or HSL.  
Lets also say it can be component-wise.
So you have:
RGB
Red
Green
Blue
Hue
Saturation
Lightness
Luminosity.

If someone wants to animate from hue to lightness, no matter how slowly you
interpret that it should *never* equal saturation.  So a nieve interpolation
is just inappropriate.  You *have* to know the datatype to know what types
of interpolations are available.  OR rather, they are all doubles but the mapping
function back to core datatype values can change per-animation.

Ditto for something like radians or degrees where the range should wrap around.

So really you need a very rich typesystem and you need to dynamically change the
systems based on this typesystem.  Also, you can't necessarily look at a single
component.  Finally, in some crazy cases you may want to do things like perform
a mapping from one range to another using a function and present that results
to render.  Actually, for any given data vertex, you should be able to provide
a remapping system that then presents said vertex differently to the rest
of the system.  Or rather, there will need to be a way for adding a layer
of interpretation above the default data graph that aliases the data graph
to the rest of the system.

The question is how does the data work?  I will have a series of transform objects,
and I want to have a derived property named transform.  This property is not saved
into the file, so it will not be at the lowest level.  It is still a property vertex,
however.  

Ideally there can be layers of graphs.  You should be able to add derived properties
without having them saved to the file.  But you should only need to add them once.

A derived property has merely a function that takes and object and returns another
object.  There will be no caching at this level of the graph.

A cache will require being notified when a given property changes.  You will need
to be able to set (and remove) a notification hook at both the schema level and at the instance
level.  To calculate global and local transforms, for instance, you need to be able
to add hooks into the transform stack *and* into your parent's global transform.  For
a lot of these, however, it may make sense to have a separate graph that is built
and updated.

The idea (and question) is this:  It is most efficient to lazy-generate various different
objects like global and local transforms.  If I can make a separate, minimal evaluation graph
used to force these evaluations then I can evaluate them in parallel using multiple cores.
I will want to be able to take a given scene and alias it efficiently to apply animations 
at different times and such.  Thus I will want to create property dependencies and additional
properties in a different context; thus I will need an idea of a graph that is the same
as another graph but you can append to it without appending to the original graph.

I will need to think about that for a while.
*)


type DataGraphVertex(m_vertex:ComplexVertexHandle) = 
    inherit Vertex()
    member s.ComplexHandle = m_vertex
    
type DataGraphVertexHandle = DataGraphVertex Handle 
    
//Used for properties that point to reference vertexes and reference list vertexes.
type DataGraphRefEdge(inc,out,m_prop:PropertyVertexHandle) = 
    inherit Edge(inc,out)
    member s.Property = m_prop
    
type DataGraphRefEdgeHandle = DataGraphRefEdge Handle

///There needs to be a mapping layer between the data graph and the data model
///This mapping layer is responsible for the richer type system of the lowest
///level data model and contains things like default values per a given type.

type IComplexListProperty =
    abstract Types : ComplexVertexHandle seq
    //Creates a new type of the first member of the types sequence and adds it.
    abstract AddFirstType : unit -> DataGraphVertexHandle
    abstract Add : hdl:DataGraphVertexHandle -> unit
    abstract Remove : DataGraphVertexHandle -> unit
    interface DataGraphVertexHandle seq
    
///Given a property, these are the possible access paths.
type GraphPropertyAccess =
    | DataAccess of (unit->obj)*(obj->unit)
    | ComplexAccess of (unit->DataGraphVertexHandle option)*(DataGraphVertexHandle option->unit)
    | ComplexListProperty of IComplexListProperty
        
type IDataGraph =
    abstract CreateDataVertex : hdl:ComplexVertexHandle -> DataGraphVertexHandle
    //Returns an enumeration of property name to property value.
    //This checks the property values in the sense that it will not traverse conditional vertexes
    //that fail their respective conditional checks.
    abstract Properties : hdl:DataGraphVertexHandle -> PropertyVertexHandle seq
    abstract Property : hdl:DataGraphVertexHandle*name:string -> PropertyVertexHandle option
    //Get a direct getter/setter to a data property value of a given object
    abstract PropertyAccess : hdl:DataGraphVertexHandle*prop:PropertyVertexHandle -> GraphPropertyAccess
    
type DataGraphProperty = { Vertex:DataGraphVertexHandle; Property:PropertyVertexHandle; PropertyAccess:GraphPropertyAccess; DataHandle:DataModelDataEntryHandle option }

type DataGraphPropertyCollection = MultiCollection<DataGraphProperty>

type DataGraphPropertyVertexIndex() =
    inherit MultiCollectionIndex<DataGraphProperty,PropertyVertexHandle*DataGraphVertexHandle>(fun(entry)-> Some (entry.Property,entry.Vertex) )
    
type DataGraphPropertyDataIndex() =
    inherit MultiCollectionIndex<DataGraphProperty,DataModelDataEntryHandle>(fun(entry)-> entry.DataHandle )
    
type DataGraphData = { DataModel:IDataModel; Graph:IDirectedGraph; SchemaGraph:ISchemaGraph; HandleSystem:ObjectHandleSystem;
    PropertyCollection:DataGraphPropertyCollection;PropertyVertexIndex:DataGraphPropertyVertexIndex;
    PropertyDataIndex:DataGraphPropertyDataIndex; BackingStore:DirectedGraphData }

let CreateDataGraphData (data,schema,func) =
    let graphData = CreateDirectedGraphData func
    let system = graphData.HandleSystem
    let graph = CreateDirectedGraph graphData
    let properties = MultiCollection<DataGraphProperty>()
    let vertexIndex = DataGraphPropertyVertexIndex()
    let dataIndex = DataGraphPropertyDataIndex()
    properties.AddSubCollection vertexIndex
    properties.AddSubCollection dataIndex
    { DataModel=data; Graph=graph; SchemaGraph=schema; HandleSystem=system;
      PropertyCollection=properties;PropertyVertexIndex=vertexIndex;PropertyDataIndex=dataIndex
      ;BackingStore=graphData }


let CreateDataGraph (data:DataGraphData) =
    let GetOrCreateObjHandle = data.HandleSystem.GetOrCreateObjHandle
    let GetHandleObj = data.HandleSystem.GetHandleObj
    let ToHandle value = data.HandleSystem.ToHandle value
    let ToHandleSeq valueSeq = data.HandleSystem.ToHandleSeq valueSeq
    let FromHandle hdl = data.HandleSystem.FromHandle hdl
    let FromHandleSeq hdlSeq = data.HandleSystem.FromHandleSeq hdlSeq
    let addv vert = 
        data.Graph.AddVertex (ToHandle (vert :> Vertex))
        ToHandle vert
    let adde edge =
        data.Graph.AddEdge (ToHandle (edge :> Edge))
        ToHandle edge
    let ToVertexHandle (hdl:#Vertex Handle) = Handle<Vertex>(hdl.Handle)
    let ToEdgeHandle (hdl:#Edge Handle) = Handle<Edge>(hdl.Handle)
    
    let AddReference (vert:DataGraphVertexHandle) (child:DataGraphVertexHandle) prop =
        let childType = (FromHandle child).ComplexHandle
        let ptype = data.SchemaGraph.PropertyDatatype prop
        let clist = 
            match ptype with
                | ComplexListDatatype(clist) -> clist
                | ComplexDatatype(clist) -> clist
                | _ -> failwith "Property is not a complex datatype"
        let firstMatch = clist |> Seq.first( fun hdl -> if hdl = childType then Some hdl else None )
        match firstMatch with
            | None -> failwith "Invalid property type"
            | Some item -> 
                //Proceed to add the reference edge
                adde (DataGraphRefEdge((ToVertexHandle vert),(ToVertexHandle child),prop))
                
    let RemoveReference (vert:DataGraphVertexHandle) (child:DataGraphVertexHandle) (prop:PropertyVertexHandle) =
        let vert = FromHandle vert
        let itemsToRemove = vert.OutgoingEdges |> Seq.filter( fun edge -> 
            let realEdge = (FromHandle edge) :?> DataGraphRefEdge
            realEdge.Property = prop && realEdge.OutgoingVertex = (ToVertexHandle child) )
        System.Collections.Generic.List(itemsToRemove) |> Seq.iter( fun(edge)->
            data.Graph.RemoveEdge edge )
    
    let PropertyAccess (vertHdl:DataGraphVertexHandle) (propHdl:PropertyVertexHandle) =
        let name = data.SchemaGraph.Name (SchemaGraphNameable.PropertyVertex(propHdl))
        let dictVal = propHdl,vertHdl
        //Do the quick lookup
        let success,existing = data.PropertyVertexIndex.Items.TryGetValue dictVal
        let vert = FromHandle vertHdl;
        let FindMatchingComplex() =
            vert.OutgoingEdges
            |> Seq.first( fun(edge)->
                let realEdge = (FromHandle edge):?>DataGraphRefEdge
                if realEdge.Property = propHdl then Some (ToHandle ((FromHandle realEdge.OutgoingVertex) :?> DataGraphVertex)) else None )
                
        let FindMatchingComplexList() = 
            vert.OutgoingEdges
            |> Seq.filter( fun(edge) ->
                let realEdge = (FromHandle edge) :?>DataGraphRefEdge
                realEdge.Property = propHdl )
            |> Seq.map( fun(edge) -> 
                let realEdge = (FromHandle edge) :?>DataGraphRefEdge
                ToHandle ((FromHandle realEdge.OutgoingVertex) :?> DataGraphVertex) )
            
        let existing = 
            if (not success) 
                then
                    let ptype = data.SchemaGraph.PropertyDatatype propHdl
                    let newEntry,dataHandle = 
                        match ptype with
                            | SimpleDatatype(name,semantic,defVal) ->
                                let dataHdl = data.DataModel.Create(semantic,defVal)
                                let getter = fun() -> data.DataModel.GetData dataHdl
                                let setter arg = data.DataModel.SetData (dataHdl,arg)
                                DataAccess(getter,setter),Some dataHdl
                            | ComplexDatatype(_) -> 
                                let SetMatchingComplex (dgv:DataGraphVertexHandle option) =
                                    let existing = FindMatchingComplex()
                                    match existing with
                                        | Some vtx -> RemoveReference vertHdl vtx propHdl
                                        | None -> ()
                                    match dgv with
                                        | Some vtx -> (AddReference vertHdl vtx propHdl) |> ignore
                                        | None -> ()
                                GraphPropertyAccess.ComplexAccess(FindMatchingComplex, SetMatchingComplex), None
                            | ComplexListDatatype(compSeq) -> 
                                let newProp = 
                                    { new IComplexListProperty with
                                        member s.Types  = compSeq
                                        member s.AddFirstType() = 
                                            let firstItem = compSeq |> Seq.first( fun(item)->Some item )
                                            match firstItem with
                                                | Some item -> 
                                                    let newv = addv (DataGraphVertex item)
                                                    AddReference vertHdl newv propHdl |> ignore
                                                    newv
                                                | None -> failwith "Property doesn't have any types"
                                             
                                        member s.Add hdl = AddReference vertHdl hdl propHdl |> ignore
                                        member s.Remove hdl = RemoveReference vertHdl hdl propHdl |> ignore
                                        end
                                        interface System.Collections.IEnumerable with
                                            member s.GetEnumerator() = FindMatchingComplexList().GetEnumerator() :> System.Collections.IEnumerator
                                        end
                                        interface System.Collections.Generic.IEnumerable<DataGraphVertexHandle> with
                                            member s.GetEnumerator() = FindMatchingComplexList().GetEnumerator()
                                        end
                                     }
                                GraphPropertyAccess.ComplexListProperty(newProp), None
                                            
                    let newEntry = { Vertex=vertHdl; Property=propHdl; PropertyAccess=newEntry; DataHandle=dataHandle }
                    data.PropertyCollection.Add newEntry
                    data.PropertyVertexIndex.Items.[dictVal]
                else
                    existing
        existing.PropertyAccess
    
    let rec Properties (vertHdl:DataGraphVertexHandle) (compHandle:ConditionalOrCompHandle) =
        seq {
            for prop in data.SchemaGraph.Properties compHandle do
                match prop with
                    | PropertyTypeHandle.PropertyVertex(hdl)-> yield hdl
                    | PropertyTypeHandle.ConditionalVertex(vtx) ->
                        let conditional = data.SchemaGraph.ConditionalData vtx 
                        let inputs = data.SchemaGraph.ConditionalInputs vtx
                        match conditional with
                            | Conditional.EqualConditional(compItem) -> 
                                let firstNonMatch = 
                                    inputs 
                                    |> Seq.first( fun(input,inputType) ->
                                        let pval = PropertyAccess vertHdl input
                                        match pval with
                                            | DataAccess(get,set) -> 
                                                let item = get()
                                                if (item <> compItem) then Some item else None
                                            | _ -> failwith "Unexpected complex data value in conditional" )
                                match firstNonMatch with
                                    | None -> yield! Properties vertHdl (ConditionalOrCompHandle.ConditionalHandle vtx)
                                    | Some nmatch -> yield! []
        }
        
                
    let Properties (vertHdl:DataGraphVertexHandle) = 
        let vert = FromHandle vertHdl
        Properties vertHdl (ConditionalOrCompHandle.ComplexHandle vert.ComplexHandle)
        |> Seq.map(fun(hdl)-> hdl) 
        
    let retval = { new IDataGraph with
        member s.CreateDataVertex hdl = addv (DataGraphVertex hdl)
        member s.Properties hdl = Properties hdl
        member s.Property (hdl,name) = 
            (Properties hdl) 
            |> Seq.first( fun item -> 
                let pname = data.SchemaGraph.Name( SchemaGraphNameable.PropertyVertex( item ) )
                if ( pname = name ) then Some item else None )
        member s.PropertyAccess (hdl,prop) = PropertyAccess hdl prop
    }
    retval
   
type GraphDatabaseData = { Data:DataModelData; Symbols:SymbolTableData
    ; Schema:SchemaGraphData
    ; Graph:DataGraphData }
    
type GraphDatabase = { Data:IDataModel; Symbols:ISymbolTable; Schema:ISchemaGraph; Graph:IDataGraph }
    
let CreateGraphDatabase() =
    let current = ref 0;
    let incFunc() = 
        current := !current + 1
        !current
    let dataStore = CreateDataModelData incFunc
    let data = CreateDataModel(dataStore)
    let symbolData = CreateSymbolTableData incFunc
    let symbols = CreateSymbolTable symbolData
    let schemaData = CreateSchemaGraphData data symbols incFunc
    let schema = CreateISchemaGraph schemaData
    let graphData = CreateDataGraphData(data,schema,incFunc)
    let graph = CreateDataGraph graphData
    let (dbaseData:GraphDatabaseData) = { Data=dataStore; Symbols=symbolData; Schema=schemaData; Graph=graphData }
    { Data=data; Symbols=symbols; Schema=schema; Graph=graph },dbaseData
    
type DataGraphTypes = 
    | Unknown = 0
    | DataGraphVertex = 1
    | DataGraphRefEdge = 2
    
let WriteDataGraph (data:DataGraphData) (writer:IDataWriter) =
    let ints = System.Collections.Generic.List<int>()
    ///We only need to save the data handle properties.
    data.PropertyCollection |> Seq.iter( fun entry ->
        match entry.DataHandle with
            | Some hdl ->
                ints.Add entry.Vertex.Handle 
                ints.Add entry.Property.Handle 
                ints.Add hdl.Handle
            | _ -> () );
    writer.Ints <| ints.ToArray()
                
    let vertWriter (item:Vertex) (output:int->unit) = 
        match item with
            | :? DataGraphVertex as vert ->
                output <| int(DataGraphTypes.DataGraphVertex)
                output <| vert.ComplexHandle.Handle
            | _ -> failwith "Unrecognized vertex type"
            
    let edgeWriter (item:Edge) (output:int->unit) =
        match item with
            | :? DataGraphRefEdge as edge ->
                output <| int(DataGraphTypes.DataGraphRefEdge)
                output <| edge.Property.Handle
            | _ -> failwith "Unrecognized edge type"
    
    WriteDirectedGraphData data.BackingStore writer vertWriter edgeWriter
    
let ReadDataGraph (data:DataGraphData) (reader:IDataReader) =
    let ints = reader.Ints().GetEnumerator()
    let nextInt() = 
        ints.MoveNext() |> ignore
        ints.Current
    while( ints.MoveNext() ) do
        let vtx = ints.Current
        let prop = nextInt()
        let dataHdl = Handle<DataModelDataEntry>( nextInt() )
        let getter = fun() -> data.DataModel.GetData dataHdl
        let setter arg = data.DataModel.SetData (dataHdl,arg)
        let access = DataAccess(getter,setter)
        let prop = { Vertex=Handle<DataGraphVertex>(vtx); 
                        Property=Handle<PropertyVertex>(prop); 
                        DataHandle = Some dataHdl;
                        PropertyAccess = access }
        data.PropertyCollection.Add prop
    
    
    let storeObj hdl arg = data.HandleSystem.StoreObject hdl (arg :> obj)
    let readVert (vhandle:int) (input:unit->int) =
        let (vtype:DataGraphTypes) = enum(input())
        let storeObj arg = 
            storeObj vhandle arg
            arg :> Vertex
        match vtype with
            | DataGraphTypes.DataGraphVertex -> 
                let chandle = Handle<ComplexVertex>(input())
                storeObj <| DataGraphVertex( chandle )
            | _ -> failwith "Unrecognized vertex type"
            
    let readEdge (ehandle:int) (inc:VertexHandle) (out:VertexHandle) (input:unit->int) =
        let (etype:DataGraphTypes) = enum(input())
        let storeObj arg = 
            storeObj ehandle arg
            arg :> Edge
        match etype with
            | DataGraphTypes.DataGraphRefEdge ->
                let phandle = Handle<PropertyVertex>(input())
                storeObj <| DataGraphRefEdge( inc, out, phandle )
            | _ -> failwith "Unrecognized edge type"
    ReadDirectedGraphData reader readVert readEdge
    

let WriteGraphDatabase( data:GraphDatabaseData, writer:IDataWriter ) =
    let symbols = 
        data.Symbols.HandleSystem.ObjectMap 
        |> Seq.map( fun entry -> 
            let symbol = (entry.Object :?> INamedObject) 
            symbol.Name,entry.Handle)
    WriteSymbolTable symbols writer
    let datas = 
        data.Data.HandleSystem.ObjectMap
        |> Seq.map( fun entry -> entry.Handle,(entry.Object :?> DataModelDataEntry) )
    WriteDataModel datas writer 
    WriteSchemaGraph data.Schema writer
    WriteDataGraph data.Graph writer
    
let ReadGraphDatabase (data:GraphDatabaseData, reader:IDataReader) =
    ReadSymbolTable reader
    |> Seq.iter( fun (sym,hdl) -> data.Symbols.HandleSystem.StoreObject hdl ((Symbol(sym)):>obj) )
    ReadDataModel reader
    |> Seq.iter( fun (hdl,entry) -> data.Data.HandleSystem.StoreObject hdl (entry:>obj) )
    ReadSchemaGraph data.Schema.HandleSystem reader
    |> Seq.iter( fun (hdl,vtx) -> data.Schema.BackingStore.Vertexes.Add (Handle<Vertex>(hdl)))
    ReadDataGraph data.Graph reader
    |> Seq.iter( fun (hdl,vtx) -> data.Graph.BackingStore.Vertexes.Add (Handle<Vertex>(hdl)))
    